package com.example.userdemo.aspect;

import com.example.userdemo.annotation.LogOperation;
import com.example.userdemo.entity.OperationLog;
import com.example.userdemo.repository.OperationLogRepository;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.time.LocalDateTime;
import java.lang.reflect.Method;
import java.util.Optional;

@Aspect
@Component
public class LogAspect {

    @Autowired
    private OperationLogRepository operationLogRepository;

    @Autowired
    private ObjectMapper objectMapper;

    // 切入点：所有使用@LogOperation注解的方法
    @Pointcut("@annotation(com.example.userdemo.annotation.LogOperation)")
    public void logPointCut() {}

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long beginTime = System.currentTimeMillis();

        // 执行方法
        Object result = joinPoint.proceed();

        // 执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;

        // 保存日志
        saveLog(joinPoint, time, result);

        return result;
    }

    private void saveLog(ProceedingJoinPoint joinPoint, long time, Object result) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        OperationLog operationLog = new OperationLog();

        // 获取注解
        LogOperation logOperation = method.getAnnotation(LogOperation.class);
        if (logOperation != null) {
            operationLog.setOperation(logOperation.value());
        }

        // 请求方法信息
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        operationLog.setMethod(className + "." + methodName + "()");

        // 请求参数
        Object[] args = joinPoint.getArgs();
        try {
            if (args != null && args.length > 0) {
                String params = objectMapper.writeValueAsString(args[0]);
                operationLog.setRequestParams(params);
            }
        } catch (Exception e) {
            operationLog.setRequestParams("参数序列化失败");
        }

        // 请求结果
        if (result != null) {
            try {
                String res = objectMapper.writeValueAsString(result);
                operationLog.setResult(res.length() > 1000 ? res.substring(0, 1000) : res);
            } catch (Exception e) {
                operationLog.setResult("结果序列化失败");
            }
        }

        // 请求时间
        operationLog.setCreateTime(LocalDateTime.now());

        // IP地址
        HttpServletRequest request = getHttpServletRequest();
        if (request != null) {
            operationLog.setIpAddress(getIpAddress(request));

            // 从Token或Session中获取用户ID (实际项目需实现)
            // String userId = request.getHeader("X-User-ID");
            // operationLog.setUserId(Long.parseLong(userId));
        }

        // 保存日志
        operationLogRepository.save(operationLog);
    }

    private HttpServletRequest getHttpServletRequest() {
        return Optional.ofNullable(RequestContextHolder.getRequestAttributes())
                .filter(attributes -> attributes instanceof ServletRequestAttributes)
                .map(attributes -> ((ServletRequestAttributes) attributes).getRequest())
                .orElse(null);
    }

    private String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}
